`use strict`

//创建渲染元素
var createVirtuallyElement = function (assembly,dom,parentElement,Element){
    let childNodes = [];
    for(let i=0;i<dom.length; i++){
        
        if(dom[i].nodeType == 3){
            childNodes = [...childNodes,...assembly.createText(dom[i])];
            continue;
        }

        let newElement = new Element(dom[i]);

        if(dom[i].nodeType == 1){
            newElement.childNodes = assembly.createVirtuallyElement(dom[i].childNodes,newElement,Element);
        }

        childNodes.push(newElement);
        newElement = null;
    }

    childNodes.forEach((value)=>{
        value.parent = parentElement;
        value.assembly = assembly;
    })
    
    return childNodes;
}

//创建元素
var createElement = function (element){
    let dom = document.createElement(element.nodeName);
    if(element.attributes){
        for(let i = 0; i<element.attributes.length;i++){
            dom.setAttribute(element.attributes[i].nodeName,element.attributes[i].nodeValue);
        }
    }
    return dom;
}

//插入元素
var insert = function (element,dom){
    if(!element.parent){
        element.assembly.Vue4.dom.append(dom);
    }else{
        element.parent.dom.append(dom);
    }
}

//修改元素文本内容
var setElementText = function (dom,text){
    dom.nodeValue = text;
}


//VUE
class Vue4{
    AssemblyList = [];
    AssemblyListText = {};
    dom = null;;

    constructor(){
        this.dom = document.createElement('div');
        this.dom.id = 'app';
        
        document.body.append(this.dom);
    }

    async createPage(AssemblySrc){
        let template = await this.createAssembly(AssemblySrc);
    }

    async createAssembly(AssemblySrc){

        if(!this.AssemblyListText[AssemblySrc]){
            let res = await axios.get(AssemblySrc);
            this.AssemblyListText[AssemblySrc] = res.data;
        }

        let newAssembly = new Assembly({text:this.AssemblyListText[AssemblySrc],Vue4:this});
        return newAssembly;
    }
}

//元素
class Element{

    nodeName = '';
    nodeType = 0;
    parent = null;
    randomID = '#'+(Date.now()+Math.ceil(Math.random()*99999));     
    assembly = null;
    innerHTML = null;
    nodeValue = null;
    refNodeValue = null;//被渲染过的实际输出文本
    attributes = {};

    refActiveList = [];
    childNodes = [];

    dom = null;

    constructor(domData){
        this.nodeName = domData.nodeName;
        this.nodeType = domData.nodeType;
        this.innerHTML = domData.innerHTML;
        this.nodeValue = domData.nodeValue;

        this.refNodeValue = this.nodeValue;
        this.attributes = domData.attributes;

    }
    
    renderingNodeValue(text){
        this.refNodeValue = text;

        if(this.dom){
            setElementText(this.dom,text)
        }

    }
    
    appendToParent(dom){
        if(!this.parent){
            this.assembly.Vue4.dom.append(dom);
        }else{
            this.parent.dom.append(dom);
        }
    }

    rendering(){
        let dom;
        switch (this.nodeType){
            case 3:
                dom = document.createTextNode(this.refNodeValue);
            break;
            case 1:
                dom = createElement(this);
                
            break;
        }
        this.dom = dom;

        if(dom)insert(this,dom);
       
        dom = null;
    }
    
}


//变量
class RefImpl{
    proxy = null;
    assembly = null;
    refWork = [];

    constructor(jsonObject){
        this.proxy = this.createProxy(jsonObject,true,'');
    }

    executeWork(){

        for(let key in this.refWork){

            let text = eval(`this.proxy${this.refWork[key].key}`);
            if(typeof(text) == 'object')text = JSON.stringify(text);
            this.refWork[key].element.renderingNodeValue(text);

        }
        
        this.assembly.executeNextTickList();

    }

    addWork(data){
        let key = data.nodeValue.split('.');
        key.splice(0,1);
        key = key.join('.');

        if(key!=''){
            key = '.v.'+key
        }else{
            key = '.v'
        }

        data.refActiveList.forEach((item)=>{

            let json = {...item,
                key:key,
                element:data
            };

            this.refWork.push(json);
        })


        this.executeWork();
      
    }

    createProxy(jsonObject,isV,name){
        if(isV)jsonObject  = {"v":jsonObject};

        if (typeof jsonObject === 'object') {
            for (let key in jsonObject) {
                if (typeof jsonObject[key] === 'object') {
                    jsonObject[key] =  this.createProxy(jsonObject[key],false,name+'.'+key);
                }
            }
        }
        
        jsonObject = Object.defineProperty(jsonObject, 'RefImpl', {
            value: this,
            writable: false
        });

        jsonObject = Object.defineProperty(jsonObject, 'name', {
            value: name,
            writable: false
        });

        
        return new Proxy(jsonObject, {
            set:  (target, key, value, receiver) =>{
                
                if (typeof value === 'object') {
                    value = this.createProxy(value,false,name+'.'+key);
                }

                setTimeout(()=>{
                    target.RefImpl.executeWork();
                },1);

                return Reflect.set(target, key, value, receiver);
            }
        })
    }


    get value(){
        return this.proxy.v;
    }

    set value(data){
        this.proxy.v = data;

        return data;
    }
}

//简化版分析HTML
function analyzeHTML(html,domList=[]){

    let regExpFindTags = /<([\s\S|=|"]*?)>[\s\S\w\W]*?<\/\1>/;

    html = html.replace(regExpFindTags,function (str){
        let dom = {childNodes:[],nodeType:1};


        str.replace(/<([\s\S]*?)>/,function (str){
            dom.nodeName = str.replace(/<|>/g,'')
            return str;
        });

        

        let regExpText = RegExp(`<${dom.nodeName}>|<\/${dom.nodeName}>`,"g")
        dom.innerHTML = str.replace(regExpText,'');

        if(dom.nodeName!='style' && dom.nodeName !='template' && dom.nodeName !='script'){
            let identifyingText = 'lelfkdkfdsk9';
            let newTetx = dom.innerHTML.replace(regExpFindTags,function (str){
                return identifyingText+str+identifyingText
            });

            let childNodesArr = newTetx.split(identifyingText);
            let childNodes = [];
            for(let key in childNodesArr){
                let isElement = false;

                childNodesArr[key].replace(regExpFindTags,function (str){
                    isElement = true;
                });

                if(isElement){
                    childNodes.push(analyzeHTML(childNodesArr[key]));
                }else{
                    childNodes.push({
                        nodeType:3,
                        nodeValue:childNodesArr[key],
                        nodeName:"#text"
                    })
                }
                
            }

            dom.childNodes = [...dom.childNodes,...childNodes]

        }else{
            dom.innerHTML.replace(regExpFindTags,function (str){
                dom.childNodes = analyzeHTML(dom.innerHTML);
            })
        }

        

        domList.push(dom);
        return ''; 
    });


    html.replace(regExpFindTags,function (str){
        domList = [...analyzeHTML(html,domList)];
    })
    return domList;
}


//组件
class Assembly{ 

    elementList = [];
    Vue4 = null;

    lifecycle = {
        "beforeMount":[]
    };

    nextTickList = [];

    constructor(data){
        this.Vue4 = data.Vue4;

        let html = analyzeHTML(data.text);


        let style = document.createElement('style');
        style.innerHTML = html[0].innerHTML;
        document.body.append(style);

        const onBeforeMount =  (call)=>{
            this.lifecycle['beforeMount'].push(call);
        }

        const nextTick =  (call)=>{
            this.nextTickList.push(call);
        }

        const ref = (data)=>{ 
            let ref = new RefImpl(data);
            ref.assembly = this;
            return ref;
        }
        
        this.elementList = [...this.createVirtuallyElement(html[1].childNodes,null,Element)];
        
        let script = html[2].innerHTML;

        
        script += `
        
        function bindDomAndRef(elementList){
            for(let i = 0;i<elementList.length;i++){
                
                if(elementList[i].refActiveList.length > 0){
                    let variable = eval((elementList[i].nodeValue).split('.')[0]);
                    variable.addWork(elementList[i]);
                }

                if(elementList[i].childNodes.length > 0){
                    bindDomAndRef(elementList[i].childNodes);
                }
            }
        }

        bindDomAndRef(this.elementList);
        
        this.rendering(this.elementList);

        `
        
        let newScript = 'function (ref,nextTick,onBeforeMount){'+script+'}';
        Function('`use strict`; return '+newScript )().call(this,ref,nextTick,onBeforeMount);

        
        

        this.lifecycle['beforeMount'].forEach((call)=>{
            call();
        })
        //this.executeNextTickList();
        
    }

    executeNextTickList(){
        this.nextTickList.forEach((item)=>{
            item();
        });
        this.nextTickList = [];
    }

    createText(dom){
        let childNodes = [];

        let identifyingStr = '*&^^*8sj^%%&*7).jsu'

        let newSt = dom.nodeValue.replace(/{{(.+?)}}/g,function (str){
            let r = '{{'+(str.replace(/{{|}}/g,''))+identifyingStr+'}}';
            return r; 
        });


        newSt.split(/{{(.+?)}}/g).forEach((value,index)=>{
            let newValue = value.replace(identifyingStr,'');


            let elementObject = new Element({
                'nodeName':'#text',
                'nodeType':3,
                'nodeValue':newValue
            });

            if(value.indexOf(identifyingStr) !== -1){
                elementObject.refActiveList.push({
                    'active':'text'
                })
            }

            childNodes.push(elementObject)
        })

        return childNodes;
    }

    createVirtuallyElement(dom,parentElement){

        return createVirtuallyElement(this,dom,parentElement,Element);
        
    }

    rendering(elementList){
        for(let key in elementList){
            elementList[key].rendering();

            if(elementList[key].childNodes && elementList[key].childNodes.length > 0){
                this.rendering((elementList[key].childNodes))
            }
        }
        
    }
}

//创建APP
const createApp = function (AssemblySrc){
    let app = new Vue4();
    app.createPage(AssemblySrc);
    return app;
}

//创建渲染器
const createRenderer = function (data){
    createVirtuallyElement = data.createVirtuallyElement;
    createElement = data.createElement;
    insert = data.insert;
    setElementText = data.setElementText;
}

export  {
    createApp,
    createRenderer 
}

